import { Injectable } from '@nestjs/common'
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'
import { isEmpty } from 'lodash'
import { EntityManager, Repository } from 'typeorm'

import { BusinessException } from '~/common/exceptions/biz.exception'
import { ErrorEnum } from '~/constants/error-code.constant'
import { DeptEntity } from '~/modules/system/dept/dept.entity'
import { UserEntity } from '~/modules/system/user/user.entity'

import { buildTree, deleteEmptyChildren } from '~/utils/tree.util'

import { DeptQueryDto, DeptSaveDto, DeptSimpleDto, MoveDept } from './dept.dto'

@Injectable()
export class DeptService {
  constructor(
    @InjectRepository(UserEntity)
    private userRepository: Repository<UserEntity>,
    @InjectRepository(DeptEntity)
    private deptRepository: Repository<DeptEntity>,
    @InjectEntityManager() private entityManager: EntityManager,
  ) {}

  async list(): Promise<DeptEntity[]> {
    return this.deptRepository.find({
      order: { sort: 'DESC' },
    })
  }

  async info(id: number): Promise<DeptEntity> {
    const dept = await this.deptRepository.findOneBy({ id })

    if (isEmpty(dept))
      throw new BusinessException(ErrorEnum.DEPARTMENT_NOT_FOUND)

    return dept
  }

  async create({ parentId, ...data }: DeptSaveDto): Promise<void> {
    const parent = parentId
      ? await this.deptRepository.findOneBy({ id: parentId })
      : null

    const dept = this.deptRepository.create({
      ...data,
      parentId: parentId || null,
    })

    await this.deptRepository.save(dept)
  }

  async update(id: number, { parentId, ...data }: DeptSaveDto): Promise<void> {
    const item = await this.deptRepository.findOneBy({ id })

    if (!item)
      throw new BusinessException(ErrorEnum.DEPARTMENT_NOT_FOUND)

    Object.assign(item, data)
    item.parentId = parentId || null

    await this.deptRepository.save(item)
  }

  async delete(id: number): Promise<void> {
    await this.deptRepository.delete(id)
  }

  /**
   * 移动排序
   */
  async move(depts: MoveDept[]): Promise<void> {
    await this.entityManager.transaction(async (manager) => {
      await manager.save(depts)
    })
  }

  /**
   * 根据部门查询关联的用户数量
   */
  async countUserByDeptId(id: number): Promise<number> {
    return this.userRepository.countBy({ dept: { id } })
  }

  /**
   * 查找当前部门下的子部门数量
   */
  async countChildDept(id: number): Promise<number> {
    return this.deptRepository.countBy({ parentId: id })
  }

  /**
   * 获取部门列表树结构
   */
  async getDeptTree(
    uid: number,
    { name }: DeptQueryDto,
  ): Promise<any[]> {
    if (name) {
      // 按名称搜索时，使用模糊查询
      const deptList = await this.deptRepository
        .createQueryBuilder('dept')
        .where('dept.name like :name', { name: `%${name}%` })
        .getMany()

      // 手动构建树形结构
      return buildTree(deptList)
    }

    // 获取所有部门数据
    const allDepts = await this.deptRepository.find({
      order: { sort: 'ASC' },
    })

    console.log('all ---- ', allDepts)

    // 手动构建树形结构
    const tree = buildTree(allDepts)
    deleteEmptyChildren(tree)

    return tree
  }

  /**
   * 获取部门简单列表
   */
  async getSimpleList(): Promise<DeptSimpleDto[]> {
    const depts = await this.deptRepository.find({
      select: ['id', 'name', 'parentId'],
      order: { sort: 'ASC' },
    })

    return depts.map(dept => ({
      id: dept.id,
      name: dept.name,
      parentId: dept.parentId || 0,
    }))
  }
}
